package online.inote.dic.security.core;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import online.inote.dic.common.utils.ResultFactory;
import online.inote.dic.common.utils.ResultFactory.ResultEnum;
import online.inote.dic.common.utils.StringUtils;
import online.inote.dic.common.utils.spring.BeanFactory;
import online.inote.dic.model.user.UserEntity;
import online.inote.dic.repository.redis.RedisRepository;
import online.inote.dic.security.annotions.Security;
import online.inote.dic.security.constants.Token;

/**
 * @description: token认证拦截器
 * @author XQF.Sui
 * @created 2019年6月15日 下午1:33:41
 * @version 1.0
 */
public class TokenAuthenticationInterceptor implements HandlerInterceptor {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	private RedisRepository redisRepository = BeanFactory.getBean(RedisRepository.class);
	
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		
		if(!(handler instanceof HandlerMethod)){
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
		
		String path = request.getServletPath();
		
		logger.debug("请求路径：" + path);
		
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Class<?> clazz = handlerMethod.getBeanType();
		Method method = handlerMethod.getMethod();
		
		Security security = null;
		
		if (method.isAnnotationPresent(Security.class)) {
			logger.debug("获取方法安全认证注解, URL:[ {} ]", path);
			security = method.getAnnotation(Security.class);
		} else if (clazz.isAnnotationPresent(Security.class)) {
			logger.debug("获取类安全认证注解, URL:[ {} ]", path);
			security = clazz.getAnnotation(Security.class);
		}
		
		if (security != null) {
			if (!security.isAuthentication()) {
				return HandlerInterceptor.super.preHandle(request, response, handler);
			}
		}
		
		ResultFactory<Object> result = verificationToken(request);
		
		if (!result.isSuccess()) {
			throw new RuntimeException(result.message);
		}
		
		return HandlerInterceptor.super.preHandle(request, response, handler);
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

		String token = request.getHeader(Token.WebKey.TOKEN_KEY);
		
		if (StringUtils.isNotBlank(token)) {
			String username = TokenManagement.getUsername(token);
			
			if (StringUtils.isNotBlank(username)) {
				redisRepository.expire(Token.RedisKey.TOKEN_KEY_PREFIX + username,
						Token.RedisKey.TOKEN_KEY_EXPIRE_SECONDS);
			}
		}
		
		HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
	}
	
	/**
	 * @description Token认证
	 * @author XQF.Sui
	 * @created 2019年4月29日 下午7:23:17
	 * @since 1.0
	 * @return
	 */
	private ResultFactory<Object> verificationToken(HttpServletRequest request) {
		
		String token = request.getHeader(Token.WebKey.TOKEN_KEY);
		
		if (StringUtils.isBlank(token)) {
			return ResultFactory.create(ResultEnum.FAIL, "未获取到登录信息，请重新登录");
		}
		
		String userInfo = null;
		UserEntity user = null;
		
		try {
			DecodedJWT jwt = JWT.decode(token);
			
			Claim username = jwt.getClaim(Token.ClaimKey.USER_NAME);
            Claim privateKey = jwt.getClaim(Token.ClaimKey.USER_PRIVATE_KEY); 
			
			if (username == null || privateKey == null || StringUtils.isBlank(username.toString())
					|| StringUtils.isBlank(privateKey.toString())) {
				return ResultFactory.create(ResultEnum.FAIL, "认证失败，请重新登录");
			}
			
			userInfo = redisRepository.get(Token.RedisKey.TOKEN_KEY_PREFIX + username.asString());
			
			if (StringUtils.isBlank(userInfo)) {
				return ResultFactory.create(ResultEnum.FAIL, "登录超时，请重新登录");
			}
			
			user = JSON.parseObject(userInfo, UserEntity.class);
			
			if (!StringUtils.equals(privateKey.asString(), user.getPrivateKey())) {
				return ResultFactory.create(ResultEnum.FAIL, "该账号已在其他地方登录，请重新登录");
			}
		} catch (Exception e) {
			logger.error("RedisService客户信息解析失败", e);
			return ResultFactory.create(ResultEnum.FAIL, "登录异常，请重新登录");
		}
		
		// 验证 token
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(user.getPassword())).withIssuer(Token.DefaultInfo.ISSUER).build();
        
        try {
        	verifier.verify(token);
        } catch (JWTVerificationException e) {
        	logger.error("JWT认证失败", e);
        	return ResultFactory.create(ResultEnum.FAIL, "登录状态验证异常，请重新登录");
        }
		
		return ResultFactory.success();
	}
}